package xmltvdb.tvdb;

import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import static btv.tools.BTVTools.*;
import static xmltvdb.tools.Constants.*;

public class Episode 
{

    private TVDBSeries series;
    private String title;
    private Date originalAirDate;
    private String language;
    private String zap2itid, imdbid;//remote id's we can use to look up series 
    
    private int season, episode;
    
    //for multi-part episodes (meaning this episode represents more than 1 tvdb episode)
    private boolean multiPart = false;
    private boolean hasTVDBImage = false;
    
    public Episode(TVDBSeries series)
    {
        this.series = series;        
    }
    public TVDBSeries getSeries()
    {
        return series;
    }
    public String getSeriesYear()
    {
        return hasSeries() ? series.seriesYear : null;
    }
    public void setTitle(String title)
    {
        this.title = title;
    }
    public boolean hasTitle()
    {
        return valid(title);
    }
    public String getTitle()
    {
        return title;
    }
    
    public void setLanguage(String langCode)
    {
        this.language = langCode;
    }
    public String getLanguage()
    {
        return valid(language) ? language : "en";//default english
    }
    public boolean hasSeries()
    {
        return valid(series);
    }
    
    
    
    public void setOriginalAirDate(Date dt)
    {
        this.originalAirDate = dt;
    }
    public boolean hasOriginalAirDate()
    {
        return originalAirDate != null;
    }
    public Date getOriginalAirDate()
    {
        return originalAirDate;
    }
    
    public boolean setMatchingEpisodes(List<TVDBEpisode> episodes)
    {
        
        if(episodes.isEmpty())
        {
            Logger.WARN("No episodes passed in to setMatchingEpisodes()");
            return false;
        }
        
        if(episodes.size() == 1)
        {
            setMultipart(false);
            TVDBEpisode ep = episodes.get(0);
            return addTVDBSeriesEpisodeNumbers(ep.seasonNumber, ep.episodeNumber);
        }
        else//multipart
        {
            setMultipart(true);            
            String seasonEpisodeNaming = "";
            int lastSeason = -1;
            TVDBEpisode firstEpisode = null;
            for(TVDBEpisode nextEpisode : episodes)
            {                
                if(firstEpisode == null) firstEpisode = nextEpisode;
                
                //check the season/episode numbers for integrity
                if(!addTVDBSeriesEpisodeNumbers(nextEpisode.seasonNumber, nextEpisode.episodeNumber))
                {
                    Logger.ERROR( "Found a multi-part match on thetvdb.com, "
                        + "but the season and episode numbers are invalid (\""+nextEpisode.seasonNumber+"\", \""+nextEpisode.episodeNumber+"\"). "
                        + "Skipping and trying again later.");
                    return false;
                }
                if(getSeason() != lastSeason)
                    seasonEpisodeNaming += "S"+getPaddedSeasonNumber();

                //always continue to add episode numbers
                seasonEpisodeNaming += "E"+getPaddedEpisodeNumber();

                lastSeason = getSeason();
            }
            Logger.INFO( "Video's multipart ("+episodes.size()+"-part) season episodes were determined to be: " + seasonEpisodeNaming);
            setMultipartSeasonEpisodeNaming(seasonEpisodeNaming);
            
            //use the first episode's season/episode as the "default" if we need just a single-episode number
            addTVDBSeriesEpisodeNumbers(firstEpisode.seasonNumber, firstEpisode.episodeNumber);
            
            return true;
        }
    }
    
    public boolean addTVDBSeriesEpisodeNumbers(String seasonNumber, String episodeNumber)
    {        
        try
        {
            int s = Integer.parseInt(seasonNumber);
            int e = Integer.parseInt(episodeNumber);
            setSeason(s);
            setEpisode(e);
            return true;
        }
        catch(Exception x)
        {
            Logger.WARN("Could not parse season episode numbers: S"+seasonNumber+"E"+episodeNumber);
            return false;//tvdb numbers are not integers
        }
    }
    
    public void setSeason(int s)
    {
        this.season = s;
    }
    public boolean hasSeason()
    {
        return season != -1;
    }
    public int getSeason()
    {
        return season;
    }
    public void setEpisode(int e)
    {
        this.episode = e;
    }
    public boolean hasEpisode()
    {
        return episode != -1;
    }
    public int getEpisode()
    {
        return episode;
    }
    
    public void setMultipart(boolean multipart)
    {
        this.multiPart = multipart;
    }
    public boolean isMultiPart()
    {
        return multiPart;
    }
    String multiPartseasonEpisodeNaming;
    public void setMultipartSeasonEpisodeNaming(String seasonEpisodeNaming){
        this.multiPartseasonEpisodeNaming = seasonEpisodeNaming;
    }
    public String getMultipartSeasonEpisodeNaming()
    {
        return multiPartseasonEpisodeNaming;
    }
    
    public void setSeries(TVDBSeries series)
    {
        this.series = series;
    }
    public String getTVDBId()
    {
        return getSeries() == null ? null : getSeries().seriesId;
    }
    public boolean hasTVDBid()
    {
        return valid(getTVDBId());
    }
    
    public void setZap2ItId(String zap2ItId)
    {
        this.zap2itid = zap2ItId;
    }
    public void setIMDBId(String imdbId)
    {
        this.imdbid = imdbId;
    }
    public void clearRemoteIds()
    {
        zap2itid = null;
        imdbid = null;
    }
    public boolean hasRemoteId()
    {
        return valid(zap2itid) || valid(imdbid);
    }
    public LinkedHashMap<String,String> getRemoteIds()
    {
        //GetSeriesByRemoteID.php?imdbid=<imdbid>&language=<language>&zap2it=<zap2it>
        LinkedHashMap<String,String> ids = new LinkedHashMap<>();
        if(valid(zap2itid))
            ids.put(ZAP2IT_IDENTIFIER, zap2itid);
        if(valid(imdbid))
            ids.put(IMDB_IDENTIFIER, imdbid);
        
        return ids;
    }
    
    public void setHasTVDBImage(boolean hasTVDBImage)
    {
        this.hasTVDBImage = hasTVDBImage;
    }
    public boolean hasTVDBImage()
    {
        return hasTVDBImage;
    }
    //helper functions    
    
    /*
     * three numbers separated by dots,
        the first is the series or season, the second the episode number
        within that series, and the third the part number, if the programme is
        part of a two-parter.  All these numbers are indexed from zero, and
        they can be given in the form 'X/Y' to show series X out of Y series
        made, or episode X out of Y episodes in this series, or part X of a
        Y-part episode.  If any of these aren't known they can be omitted.
        You can put spaces whereever you like to make things easier to read.
     */
    public String getXMLTVSeasonEpisodeAttribute()
    {
        //zero indexed
        int e = getEpisode()-1;
        int s = getSeason()-1;
        final String part = "0/0";//not used, but needed to keep FTR parser happy
        return s+"."+e+"."+part;
    }
    public String getPaddedEpisodeNumber()
    {
        return padNumber(getEpisode());
    }
    public String getPaddedSeasonNumber()
    {
        return padNumber(getSeason());
    }
    private String padNumber(int num)
    {
        if(num < 10) return "0"+num;
        else return ""+num;
    }
    
    @Override
    public String toString(){
        return getSeries()+": S"+getPaddedSeasonNumber()+"E"+getPaddedEpisodeNumber()+" - " + getTitle()+ 
                (hasOriginalAirDate() ? " ("+TVDB.dateToTVDBString(getOriginalAirDate())+")" : "");
    }
}